<?php
namespace Core;

class DzInternetProtocol
{
    public  $token;
    public  $sendDataArr=array();//发送的数据元素

    private $_map = array(
        //管理帧
        '00'=>array(
//            '0000' => 'broadcast',
//            '0001' => 'scanAsk',
//            '0010' => 'scanAnswer',
            '0011' => 'connectAsk',
            '0100' => 'connectAnwser',
//            '0101' => 'authAsk',
//            '0110' => 'authAnswer',
            '0111' => 'CloseAsk',
            '1000' => 'CloseAnswer',
        ),
        //控制帧
        '01'=>array(
            '0000' => 'abilityAsk',
            '0001' => 'abilityResponse',
            '0010' => 'controlAsk',
            '0011' => 'controlResponse',
            '0100' => 'erroreport',
            '0101' => 'erroResponse',
            '0110' => 'statusAsk',
            '0111' => 'ststusResponse',
            '1000' => 'baseControl'
        ),
        //数据帧
        '10'=>array(
            '0000' => 'getdataReceive',
            '0001' => 'dataResopnse',
            '0010' => 'dataAsk',
        )

    );

    public  $frameHead = [
        'vs'=>'00',//版本2
        'dt'=>'00',//数据类型2
        'sdt'=>'0100',//子类型4
        'oth'=>'0000',//备注4
        'td'=>'10',//传输方向2
        'rt'=>'0',//重传标志1
        'ss'=>'0',//单包标志1
        'nm'=>'0',//序号
    ];

    public $TbParsePro;
    public function __construct()
    {
//        $this->TbParsePro=new ParseMsg();
    }

    public function getinitdata($data)
    {
        //获取消息类型再调用map映射的方法
        if(!isset($data) || !isset($data['dt']) || !isset($data['sdt']) ) return false;

        $msgType=$data['dt'];
        $msgSubType=$data['sdt'];
        if(!isset($this->_map[$msgType][$msgSubType])) return false;

        $method = $this->_map[$msgType][$msgSubType];
        return $this->$method($data);
    }


    /**
     * 原始数据帧解析
     * @param $string
     * @param string $token
     * @return bool
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/20 15:56
     */
    public function parsein($hexStr, $token=0)
    {
        if(!isset($hexStr) || empty($hexStr)) return false;
//        $hexStr = bin2hex($string);
        $this->token=!empty($token)?$token:time();
        try {
            //物联网协议，获取帧头
            $hexType = substr($hexStr, 0, 4);
            if ($hexType == '2004') {
                $framedata = array(//默认'2004'
                    'vs' => '00',//版本2
                    'dt' => '10',//数据类型2
                    'sdt' => '0000',//子类型4
                    'oth' => '0000',//备注4
                    'td' => '01',//传输方向2探霸--》服务器
                    'rt' => '0',//重传标志1
                    'ss' => '0',//单包标志1
                    'nm' => '0',//序号
                );
            }
            else{
                //帧头第一个字节
                $frameHeadstr1 = sprintf('%08d', base_convert(substr($hexStr, 0, 2), 16, 2));
                $framedata['vs'] = substr($frameHeadstr1, 0, 2);
                $framedata['dt'] = substr($frameHeadstr1, 2, 2);
                $framedata['sdt'] = substr($frameHeadstr1, 4, 4);

                //帧头第二个字节
                $frameHeadstr2 = sprintf('%08d', base_convert(substr($hexStr, 2, 2), 16, 2));
                $framedata['oth'] = substr($frameHeadstr2, 0, 4);
                $framedata['td'] = substr($frameHeadstr2, 4, 2);
                $framedata['rt'] = substr($frameHeadstr2, 6, 1);
                $framedata['ss'] = substr($frameHeadstr2, 7, 1);

                $framedata['nm'] = $framedata['ss'] == '0' ? '' : intval(sprintf('%08d', base_convert(substr($hexStr, 8, 2), 16, 10)));//单包1时第5个字节表示序号

            }

            $this->frameHead = $framedata;
            $retdata['header'] = $framedata;

            //总长度，含头尾
            $framedata['len'] = hexdec(substr($hexStr, 4, 4));

            if ($framedata['dt'] == '00' && in_array($framedata['sdt'], ['0000', '0001', '0010', '0011', '0100', '0101', '0110'])) {
//            建立阶段 校验和
                $length = $framedata['len'] - 6;//头2字节有 明文尾1字节 //$framedata['ss']=='0'?6:7
                $framebodystr = substr($hexStr, 8, $length * 2);//$framedata['ss']=='0'?8:10
//                $framedata['tail'] = substr($hexStr, -4);//明文

                $framedata['body'] = $this->subparsein($framebodystr);//解析子元素
                $retdata['body'] = $framedata['body'];
            } else {
                //md5加密的
                if(substr($hexStr, 0, 4)=='2004') {

                    $databodystr=substr($hexStr,0,-16).dechex($this->token)."00000000";//扣除校验段数据段

                    //dump(substr($hexStr, -16));
                    //dump($databodystr);
//                    echo "2-".$databodystr.PHP_EOL;
                    $md5value=md5(pack("H*",  $databodystr));
                    //dump($md5value);

//                echo "3-".$md5value."|".substr($hexStr,-16).PHP_EOL;
                    if(substr($md5value,0,16)==substr($hexStr,-16)){
//                        echo "ok".PHP_EOL;
                        $framebodystr = substr($hexStr, 8);
                        $framedata['body'] = $framebodystr;
                    }
                    else{

//                    echo "-".$hexStr."|".$this->token.PHP_EOL;
//                        echo "ERR".PHP_EOL;
//                        $retdata['data']=[];
                        $retdata['pserro']=$hexStr."|".$this->token."|".dechex($this->token);
                        return $retdata;

                    }


                }else{
                    $framebodystr = substr($hexStr, 8);
                    $framedata['body'] = $framebodystr;
                }

            }

            if ($framedata['dt'] == '01' && $framedata['sdt'] == '1000') {
                //基础控制帧是双向的，注意
                $retdata['data'] = $this->md5decode($framebodystr);
                if($framedata['td']){
                    $retdata['data']= $this->getbasecontrolinfo($retdata['data']);
                    return $retdata;
                }

            }
            else {
                $framedata['header'] = $framedata;
                $retdata['data'] = $this->getinitdata($framedata);
            }

            return $retdata;
        } catch(Exception $e) {
            echo "\n parse erro \n";
            return false;
        }

    }

    public function upgradeparsein($hexStr, $token=0)
    {
        if(!isset($hexStr) || empty($hexStr)) return false;
//        $hexStr = bin2hex($string);
        $this->token=!empty($token)?$token:time();

        //物联网协议，获取帧头
        $hexType = substr($hexStr, 0, 4);
        if ($hexType == '2004') {
            $framedata = array(//默认'2004'
                'vs' => '00',//版本2
                'dt' => '10',//数据类型2
                'sdt' => '0000',//子类型4
                'oth' => '0000',//备注4
                'td' => '01',//传输方向2探霸--》服务器
                'rt' => '0',//重传标志1
                'ss' => '0',//单包标志1
                'nm' => '0',//序号
            );
        }
        else{
            //帧头第一个字节
            $frameHeadstr1 = sprintf('%08d', base_convert(substr($hexStr, 0, 2), 16, 2));
            $framedata['vs'] = substr($frameHeadstr1, 0, 2);
            $framedata['dt'] = substr($frameHeadstr1, 2, 2);
            $framedata['sdt'] = substr($frameHeadstr1, 4, 4);

            //帧头第二个字节
            $frameHeadstr2 = sprintf('%08d', base_convert(substr($hexStr, 2, 2), 16, 2));
            $framedata['oth'] = substr($frameHeadstr2, 0, 4);
            $framedata['td'] = substr($frameHeadstr2, 4, 2);
            $framedata['rt'] = substr($frameHeadstr2, 6, 1);
            $framedata['ss'] = substr($frameHeadstr2, 7, 1);

            $framedata['nm'] = $framedata['ss'] == '0' ? '' : intval(sprintf('%08d', base_convert(substr($hexStr, 8, 2), 16, 10)));//单包1时第5个字节表示序号

        }

        $this->frameHead = $framedata;
        $retdata['header'] = $framedata;

        //总长度，含头尾
        $framedata['len'] = hexdec(substr($hexStr, 4, 4));

        $length = $framedata['len'] - 6;//头2字节有 明文尾1字节 //$framedata['ss']=='0'?6:7
        $framebodystr = substr($hexStr, 8, $length * 2);//$framedata['ss']=='0'?8:10

        $framedata['body'] = $this->subparsein($framebodystr);//解析子元素
        $retdata['body'] = $framedata['body'];

//            $framedata['header'] = $framedata;
        $framedata['header']['upgradesv'] = 1;

        $retdata['data'] =$this->getinitdata($framedata);

        return $retdata;

    }

    /**
     * 数据帧解包解析--循环解包，可能有多个
     * @param $hexStr
     * @return bool
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/20 15:53
     */
    public function subparsein($hexStr)
    {
        $string = $hexStr;
        $datas = array();
        while (true) {
            if (false == strlen($string) || empty($string)) break;

            $dtype=substr($string, 0, 4); //帧头2个字节为类型
            $offset =2 *hexdec(substr($string, 4, 4));// intval(base_convert(substr($string, 4, 4), 16, 10));
            if($offset>0){
                $dataitem = substr($string, 8, $offset);

                $string=substr($string, $offset+8);
                $datas[]=['type'=>$dtype,'len'=>$offset,'datastr'=>$dataitem];
            }else{
                $string='';
                $datas[]=['type'=>$dtype,'len'=>0,'datastr'=>$hexStr];
            }

        }

        return $datas;

    }


    /**
     * 生成发送的命令
     * @param $data 帧头信息data['header']
     * @param $param
     * @param $token
     * @return array|bool|string
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/25 11:08
     */
    public function setCommand($param,$option=[],$token=0)
    {
        if(!isset($param) || !isset($param['dt']) || !isset($param['sdt']) || !isset($option) ) return false;
        $this->token=empty($token)?0:$token;
        $data=array();

        $data['header']= is_array($param)? array_merge($this->frameHead,$param):$this->frameHead;
        $data['option']=$option;
        $method = $this->_map[$param['dt']][$param['sdt']];

        if(isset($option['do']) && $option['do']=='close')
            $encodestr=$this->closeAsk($data);
        else
            $encodestr=$this->$method($data);

        if(!$encodestr) return '0';
        return $this->makeControlCode($encodestr);

    }

    /**
     * 2进制的帧头组合转为16进制
     * @param $header
     * @return string
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/21 15:32
     */
    public function setHeader($header)
    {
        //   $headerstr=$header['vs'].$header['dt'].$header['sdt'].$header['oth']."10".$header['rt']."0";
        $headerstr =base_convert((string)($header['vs'].$header['dt']),2,16);
        $headerstr .=base_convert((string)$header['sdt'],2,16);
        $headerstr .=base_convert((string)$header['oth'],2,16);
        $headerstr .=base_convert((string)("10".$header['rt']."0"),2,16);

        return $headerstr;
    }

    /**
     * 16进制的数据元素组合串
     * @param $dataitems
     * @return array
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/21 15:32
     */
    public function setSendDataStr($dataitems)
    {
        $len=0;
        $datastr="";
        foreach ($dataitems as $key=>$val){
            if(isset($val)&& !empty($val)){
                $datastr .=$val;
                $len += strlen($val)/2;
            }
        }

        return ['data'=>$datastr,'len'=>$len];
    }

    /**
     * 根据帧头信息和主体数据信息进行加密，送给发送者
     * @param $data
     * @return array|bool|string
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/25 11:09
     */
    private function makeControlCode($data)
    {
        if(!isset($data)|| !isset($data['header'])|| !isset($data['data'])) return false;
        $header=$this->setHeader($data['header']);
        $senddata=$this->setSendDataStr($data['data']);

        if( ($data['header']['dt']=='00' && $data['header']['sdt']=='0100') || (isset($data['header']['upgradesv'])&& $data['header']['upgradesv'] == 1)  || (isset($data['option']['do']) && $data['option']['do']=='sdtoken' &&(isset($data['option']['one'])&& $data['option']['one']==0 ) )){
            $allLen=6+$senddata['len']; //4个字节头+2个字节的校验和

            //连接响应或发送token用明文
            $lenStr=sprintf("%04x",$allLen);//构造字符串长度的2字节 //16进制的4个位置不足补0，2个字节
            $datastr = $header.$lenStr.$senddata['data'];

            $datatailstr=$this->xorencode($datastr,0);//        if($data['encodeType']['code']=='XOR') {

            $datastr.=$datatailstr;
//            echo "*-----xorencode-".json_encode($data['option'])."------*\n";
        }
        else{
            $allLen=8+$senddata['len']+4; //4个字节头+2个字节长度+2个字节的校验和
            //其他要64加密 ---------------------------
            $lenStr=sprintf("%04x",$allLen);
            $datastr = $header.$lenStr.$senddata['data'];
//            echo "*----encryption start--{$datastr} |{$this->token}-------*\n";
            $datastr=$this->encryption($datastr,$this->token);
//            echo "*----encryption end--{$datastr}-------*\n";
        }

//        var_dump($datastr)
        return $this->pushhex($datastr);

    }


    /**
     * 连接请求
     * @param $data
     * @return array|bool
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/25 14:46
     */
    private function connectAsk($data)
    {
        echo "============connectAsk===========\n";
        if(!isset($data['body']) || !is_array($data['body'])) return false;
        $rdata['rawstr']='';

        foreach ($data['body'] as $vl){
            switch ($vl['type']){
                case '0003' :
                    $rdata['encodeType']['code']=substr($vl['datastr'],0,2)=='01'?'XOR':'';
                    $rdata['encodeType']['check']=substr($vl['datastr'],-2)=='01'?'MD5':'';
                    break;
                case '0011':
                    $rdata['device']['typename']=substr($vl['datastr'],2,6);

                    $retmap=['民用消费','工业控制','军工','科研','特种设备','其他'];
                    $tempstr= sprintf('%08d', base_convert(substr($vl['datastr'],0,2), 16, 2));
                    $typeid=hexdec(substr($tempstr,0,4));
                    $rdata['device']['typearea']=$typeid>4 ?$retmap[5]:$retmap[$typeid];
                    $rdata['device']['typetype']=hexdec(substr($tempstr,4,8))==0?'网关路由类':'普通设备类';

                    $rdata['device']['type']=substr($vl['datastr'],0,8);//substr($vl['data'],0,8)=='01000001'?'TB':'';
                    $rdata['device']['mac']=hex2ascii(str_split(substr($vl['datastr'],8,36), 2));

                    $rdata['fromcmd']='baseinfo';
                    break;

                default:
                    $rdata[]['data']=$vl['datastr'];
            }
            $rdata['rawstr'].=$vl['datastr'];
        }

        return $rdata;


    }

    /**
     * 连接响应
     * @param $data
     * @return array
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/25 15:20
     */
    private function connectAnwser($data)
    {
        echo "============connectAnwser===========\n";
        return array(
            'header'=>$data['header'],
            'option'=>isset($data['option'])?$data['option']:[],
            'data'=>[
                'item0'=>'000200020101',//帧加解密方式b
                'item1'=>'0004000100',//帧加解密方式d
                'item2'=>'0032000101'//连接断开标识c
            ],
            'token'=>$this->token
        );

    }

    /**
     * 获取设备信息或设备位置信息
     * @param $data
     * @return array
     * @Created by: xiebaoxin
     * @DateTime: 2018/4/28 9:43
     */
    private function statusAsk($data)
    {
        echo "============statusAsk===========\n";

        $retarry=array(
            'header'=>$data['header'],
            'option'=>isset($data['option'])?$data['option']:[],
            'token'=>$this->token
        );
        /*if($data['option']['do']=='getdeviceinfo'){
            $retarry['data']= ['item1'=>'50000000'];//cfg，data获取设备信息

        }elseif ($data['option']['do']=='getdevicestatus'){
            $retarry['data']= ['item1'=>'50010000'];//获取状态信息
        }
        elseif ($data['option']['do']=='getdeviceloc'){
            $retarry['data']= ['item1'=>'50020000'];//cfg，data获取位置信息
        }
        elseif ($data['option']['do']=='getlbslocbase'){
            echo "============getlbslocbase===========\n";
            $retarry['data']= ['item1'=>'50030000'];
        }
        elseif ($data['option']['do']=='getsmbaseinfo'){
            echo "============getsmbaseinfo===========\n";
            $retarry['data']= ['item1'=>'50040000'];
        } */

        $item = '';
        switch ($data['option']['do']) {
            case 'getdeviceinfo': // cfg，data获取设备信息
                $item = '50000000';
                break;
            case 'getdevicestatus': //获取状态信息
                $item = '50010000';
                break;
            case 'getdeviceloc': //cfg，data获取位置信息
                $item = '50020000';
                break;
            case 'getlbslocbase': //LBS 位置信息获取
                $item = '50030000';
                break;
            case 'getsmbaseinfo':
                $item = '50040000';
                break;
        }

        $retarry['data']['item1'] = $item;

        return $retarry;

    }

    private function ststusResponse($data)
    {

        echo "============ststusResponse===========\n";
        if(!isset($data['body']) ) return false;
        $retxorBody = $this->md5decode($data['body']);
        echo $retxorBody."\n";
        if(isset($retxorBody) && !empty($retxorBody)){
            $rdata=array();
            $framedata['body'] = $this->subparsein($retxorBody);//解析子元素

            foreach ($framedata['body'] as $vl){
                switch ($vl['type']){
                    case '6002' :
                    if($vl['len']==2){
                        $rdata['location']=substr($vl['datastr'],0,2);
                    }else{
                        $temptypestr= sprintf('%08d', base_convert(substr($vl['datastr'],0,2), 16, 2));
                        $rdata['location']['typestr']=$temptypestr;

                        if(substr($temptypestr,7,1)==1)$rdata['location']['type'][]='AGPS' ;
                        if(substr($temptypestr,6,1)==1)$rdata['location']['type'][]='Galieo' ;
                        if(substr($temptypestr,5,1)==1)$rdata['location']['type'][]='BEIDOU' ;
                        if(substr($temptypestr,4,1)==1)$rdata['location']['type'][]='GLONASS' ;
                        if(substr($temptypestr,3,1)==1)$rdata['location']['type'][]='GPS' ;
                        if(substr($temptypestr,2,1)==1)$rdata['location']['type'][]='LBS' ;

                        $rdata['location']['longitude']=hex2ascii(str_split(substr($vl['datastr'],2,22),2));
                        $rdata['location']['latitude']=hex2ascii(str_split(substr($vl['datastr'],24,20),2));
                        $rdata['location']['hightland']=hex2ascii(str_split(substr($vl['datastr'],44,16),2));
                    }
                    $rdata['fromcmd']='getdeviceloc';
                    break;
                    case '6001':
                        $webtpmap=['未知','WiFi','WLan','GPRS','3G','4G','其他'];
                        $webtpye=hexdec(substr($vl['datastr'],0,2));
                        $rdata['device']['webtype']=$webtpye==255?$webtpmap[6]:$webtpmap[$webtpye];//联网方式

                        $tmpmap=['0'=>0,'127'=>0,'126'=>-1,'128'=>1];
                        $temperature=hexdec(substr($vl['datastr'],2,2))-127;
                        $rdata['device']['temperature']=!isset($tmpmap[$temperature])?$temperature:$tmpmap[$temperature];//温度

                        $temperature=hexdec(substr($vl['datastr'],4,2))-127;
                        $rdata['device']['temperature2']=!isset($tmpmap[$temperature])?$temperature:$tmpmap[$temperature];//温度

                        $temperature=hexdec(substr($vl['datastr'],2,4));
                        $rdata['device']['testbattery']=$temperature;//电量测试

                        /*
                         $fanmap=['不存在','关闭','低速','中速','高速','全速'];
                         $fanstatus=hexdec(substr($vl['datastr'],4,2));
                         $rdata['device']['fanstatus']=$fanmap[$fanstatus];//风扇状态*/

                        $btysttmap=['未知','供电中','充电中','正常','没电'];
                        $btymap=['未知','100%','80%','60%','40%','20%','随时关机'];
                        $rdata['device']['battery']=$btymap[hexdec(substr($vl['datastr'],6,1))];
                        $rdata['device']['batterystatus']=$btysttmap[hexdec(substr($vl['datastr'],7,1))];//电池状态
                        $rdata['fromcmd']='getdevicestatus';
                        break;
                    case '6000':
                        $rdata['device']['string']=$vl['datastr'];
                        $parmarr=['00','采集模式','布控模式','待机模式','低功耗模式'];
                        $rdata['device']['wktype']=$parmarr[hexdec(substr($vl['datastr'],42,2))] ;//工作模式

                        $rdata['device']['vltype']=substr($vl['datastr'],40,2);//协议版本
                        $framestr = sprintf('%08d', base_convert($rdata['device']['vltype'], 16, 2));
                        $rdata['device']['subvltype']=hexdec(substr($rdata['device']['vltype'], 0, 1));
                        $rdata['device']['mvltype']=hexdec(substr($rdata['device']['vltype'], 1, 1));

                        $rdata['device']['hdtype']=substr($vl['datastr'],38,2);//硬件版本
                        $framestr = sprintf('%08d', base_convert($rdata['device']['hdtype'], 16, 2));
                        $rdata['device']['subhdtype']=hexdec(substr($rdata['device']['hdtype'], 0, 1));
                        $rdata['device']['mhdtype']=hexdec(substr($rdata['device']['hdtype'], 1, 1));

                        $rdata['device']['sttype']=substr($vl['datastr'],36,2);//软件版本
                        $framestr = sprintf('%08d', base_convert($rdata['device']['sttype'], 16, 2));
                        $rdata['device']['substtype']=hexdec(substr($rdata['device']['sttype'], 0, 1));
                        $rdata['device']['msttype']=hexdec(substr($rdata['device']['sttype'], 1, 1));

                        $rdata['device']['mac']=hex2ascii(str_split(substr($vl['datastr'],0,36), 2));
                        $rdata['fromcmd']='getdeviceinfo';
                        break;


                    case "6003": //获取地址策略
                        echo "\n ============getlbslocbase===========\n";
                        if(strlen($vl['datastr'])<=3)
                            $rdata['data'] =hexdec($vl['datastr']);
                        else
                            $rdata['data'] = hex2ascii(str_split($vl['datastr'], 2));

                        $rdata['fromcmd']='getlbslocbase';

                        break;
                    case "6004":
                        echo "\n ============getsmbaseinfo===========\n";
                        if(strlen($vl['datastr'])<=3)
                            $rdata['data'] =hexdec($vl['datastr']);
                        else
                        {
                            $rdata['data']['imei'] = hex2ascii(str_split(substr($vl['datastr'],0,30), 2));
                            $rdata['data']['meid'] = hex2ascii(str_split(substr($vl['datastr'],30,28), 2));
                            $rdata['data']['imsi'] = hex2ascii(str_split(substr($vl['datastr'],58,30), 2));
                            $rdata['data']['iccid'] = hex2ascii(str_split(substr($vl['datastr'],88,40), 2));
                        }

                        $rdata['fromcmd']='getsmbaseinfo';


                        break;

                    default:
                        $rdata[]['data']=$vl['datastr'];
                }
            }
            $rdata['rawstr']=$retxorBody;
        }

        return $rdata;


    }

    /**
     * 获取设备能力信息
     * @param $data
     * @return array
     * @Created by: xiebaoxin
     * @DateTime: 2018/4/28 9:44
     */
    private function abilityAsk($data)
    {
        echo "============abilityAsk===========\n";
        return array(
            'header'=>$data['header'],
            'option'=>isset($data['option'])?$data['option']:['do'=>'getability'],
            'data'=>[
                'item1'=>'00120000'
            ],
            'token'=>$this->token
        );

    }

    private function abilityResponse($data)
    {
        echo "============abilityResponse===========\n";
        if(!isset($data['body']) ) return false;

        $retxorBody = $this->md5decode($data['body']);
        if(isset($retxorBody) && !empty($retxorBody)){

            $rdata=array();
            $framedata['body'] = $this->subparsein($retxorBody);//解析子元素

            foreach ($framedata['body'] as $vl){
                switch ($vl['type']){
                    case '0013' :
                        $tempstr= sprintf('%08d', base_convert(substr($vl['datastr'],0,2), 16, 2));
                        /*$rdata['base']['ability']['pack'] = hexdec(substr($tempstr,5,1))==0?'支持分包':'不支持分包';
                        $rdata['base']['ability']['trans']=hexdec(substr($tempstr,6,1))==0?'支持重传':'不支持重传';
                        $rdata['base']['ability']['gps']=hexdec(substr($tempstr,7,1))==1?'GPS':'无GPS';*/

                        /**
                         * 1. 修复获取能力错误的bug
                         * 2. 增加多设备支持与否的获取
                         */
                        $rdata['base']['ability']['gps'] = $tempstr[7] == 1 ? 'GPS' : '无GPS';
                        $rdata['base']['ability']['trans'] = $tempstr[6] == 1 ?'支持重传' : '不支持重传';
                        $rdata['base']['ability']['pack'] = $tempstr[5] == 1 ? '支持分包':'不支持分包';
                        $rdata['base']['ability']['multi'] = $tempstr[4] == 1 ? '支持多设备':'不支持多设备';

                        $rdata['base']['length']=hexdec(substr($vl['datastr'],2,4));
                        $rdata['base']['rawstr']=$vl['datastr'];
                        $rdata['fromcmd']='getability';
                        break;
                    case '0011':
                        $rdata['device']['typestr']=substr($vl['datastr'],0,2);
                        $rdata['device']['typename']=substr($vl['datastr'],2,6);
                        $retmap=['民用消费','工业控制','军工','科研','特种设备','其他'];
                        $tempstr= sprintf('%08d', base_convert($rdata['device']['typestr'], 16, 2));
                        $typeid=hexdec(substr($tempstr,0,4));
                        $rdata['device']['typearea']=$typeid>4 ?$retmap[5]:$retmap[$typeid];
                        $rdata['device']['typetype']=hexdec(substr($tempstr,4,8))==0?'网关路由类':'普通设备类';

                        $rdata['device']['mac']=hex2ascii(str_split(substr($vl['datastr'],8,36), 2));
                        $rdata['device']['rawstr']=$vl['datastr'];
                        $rdata['fromcmd']='getability';
                        break;

                    default:
                        $rdata[]['data']=$vl['datastr'];
                }
            }
            $rdata['rawstr']=$retxorBody;
        }

        return $rdata;

    }

    /**
     * 设备控制请求
     * @param $data
     * @return array
     * @Created by: xiebaoxin
     * @DateTime: 2018/4/26 17:34
     */
    private function controlAsk($data)
    {
        $retarry= array(
            'header'=>$data['header'],
            'option'=>isset($data['option'])?$data['option']:[],
            'token'=>$this->token
        );

        switch ($data['option']['do']) {
            case
            "getdatatimer":
                echo "============getdatatimer===========\n";
                $itemstr = '30010000';//获取数据上报间隔
                break;
            case "setdatatimer":
                echo "============setdatatimer===========\n";
                $string1 = sprintf("%04x", isset($data['option']['ap']) ?$data['option']['ap']: 2);
                $string2 = sprintf("%04x",  isset($data['option']['sta']) ?$data['option']['sta']: 2);
                $string3 = sprintf("%04x",  isset($data['option']['rf']) ?$data['option']['rf']: 2);
                $itemstr='30000006'.$string1.$string2.$string3;//设置心跳
                break;
            case "setdatauptype"://设置数据上报方式
                echo "============setdatauptype===========\n";
                $itemstr = '30020001' . sprintf("%02x", $data['option']['data']);//设置数据上报方式
                break;
            case "getdatauptype"://获取数据上报方式 ok
                echo "============getdatauptype===========\n";
                $itemstr = '30030000';
                break;

            case "setreboot": //设置重启和复位 ok
                echo "============setreboot===========\n";
                $itemstr = '10040001'. sprintf("%02x", $data['option']['data']);
                \App\Library\Log::commonLog('')->debug('调试: 设置重启和复位', [
                    'item' => $itemstr,
                    '原始数据' => $data
                ]);
                break;
            case "setdnsport"://设置域名和端口
                echo "============setdnsport===========\n";
                if(isset($data['option']['cfgsv'])&& isset($data['option']['datasv'])){
                    $cfginfo=explode(":",$data['option']['cfgsv']);
                    $scgstring =bin2hex($cfginfo[0]);//域名字符
                    $scgstrlen=strlen($scgstring)/2;//域名长度
                    $string1 =sprintf("%02x",$scgstrlen).$scgstring.sprintf("%04x",$cfginfo[1]);

                    $datainfo=explode(":",$data['option']['datasv']);
                    $dtstring =bin2hex($datainfo[0]);
                    $datastrlen=strlen($dtstring)/2;
                    $string2 =sprintf("%02x",$datastrlen).$dtstring.sprintf("%04x",$datainfo[1]);

                    $totlelen =sprintf("%04x",$scgstrlen+$datastrlen+6);
                }

                $itemstr = '1005'.$totlelen. $string1.$string2;

                break;
            case "getdnsport": //获取域名和端口
                echo "============getdnsport===========\n";
                $itemstr = '10060000';
                break;
            case "setipport"://设置IP和端口 ok
                echo "============setipport===========\n";
                if(isset($data['option']['cfgsv'])&& isset($data['option']['datasv'])){
                    $cfginfo=explode(":",$data['option']['cfgsv']);
                    $cfgiparr=explode(".",$cfginfo[0]);
                    $string1 = sprintf("%02x",$cfgiparr[0]).sprintf("%02x",$cfgiparr[1]).sprintf("%02x",$cfgiparr[2]).sprintf("%02x",$cfgiparr[3]);
                    $string1 .=sprintf("%04x",$cfginfo[1]);

                    $datainfo=explode(":",$data['option']['datasv']);
                    $dateiparr=explode(".",$datainfo[0]);
                    $string2 = sprintf("%02x",$dateiparr[0]).sprintf("%02x",$dateiparr[1]).sprintf("%02x",$dateiparr[2]).sprintf("%02x",$dateiparr[3]);
                    $string2 .=sprintf("%04x",$datainfo[1]);
                }

                $itemstr = '1007000c'. $string1.$string2;
                break;
            case "getipport": //获取IP和端口 ok
                echo "============getipport===========\n";
                $itemstr = '10080000';
                break;
            case "setaddroptin"://设置地址策略 1域名 2IP
                echo "============setaddroptin===========\n";
                $itemstr = '10090002'. sprintf("%02x", $data['option']['cfg']). sprintf("%02x", $data['option']['data']);
                break;
            case "getaddroptin": //获取地址策略
                echo "============getaddroptin===========\n";
                $itemstr = '100A0000';
                break;

            case "setworkmode": //获取终端工作模式
                echo "============setworkmode===========\n";
                $itemstr = '100C0001'. sprintf("%02x", $data['option']['data']);
                break;

            case "setfiltermode"://设置过滤策略
                echo "============setfiltermode===========\n";
                $type = sprintf("%02x", $data['option']['type']);//1STA2AP3RF
                $macs = $data['option']['macs'];
                $maclen = sprintf("%02x", (strlen($macs) / 2));
                $len = sprintf("%04x", strlen($macs)/2+2);
                $itemstr="100D".$len.$type.$maclen.$macs;

                break;
            case "getfiltermode"://获取过滤策略 1sta 2ap 3rf
                echo "============getfiltermode===========\n";
                $itemstr = '100E0001'. sprintf("%02x", $data['option']['data']);
                break;


            case "setupdate"://设置固件升级
                echo "============setupdate===========\n";
//                var_dump($data['option']);
                $uptinfo=explode(":",$data['option']['data']['uptsv']);
                $uptiparr=explode(".",$uptinfo[0]);
                $string = sprintf("%02x",$uptiparr[0]).sprintf("%02x",$uptiparr[1]).sprintf("%02x",$uptiparr[2]).sprintf("%02x",$uptiparr[3]);
                $string .=sprintf("%04x",$uptinfo[1]);
                $string .= str_pad(str2ascii($data['option']['data']['frimware']), 60, 0, STR_PAD_RIGHT );
//                echo $string;
                $msglen=sprintf("%08x", $data['option']['data']['size']);
                $string .= $msglen;
                $string .= $data['option']['data']['xsumcode'];//sprintf("%04x", hexCalcSum($msglen));//校验和
                $string .= sprintf("%02x", $data['option']['data']['hardver']);
                $string .= sprintf("%02x", $data['option']['data']['softver']);

                $itemstr = '1010002C'.$string;
                dump('固件升级:' . $itemstr);
                break;
            case "askupdatedata"://升级服务器响应设备升级的请求 准备好了
                echo "============askupdatedata===========\n";
                $itemstr = '20110001'. sprintf("%02x", $data['option']['data']);
                break;
            case "getupdatenum"://升级服务器响应设备升级包序号的请求 发送数据包
                echo "============getupdatenum===========\n";
                $packlength = $data['option']['data']['packlength'];
                $file = trim($data['option']['data']['file']);
                $packnum =  $data['option']['data']['packnum'];
                if($packnum>0){
                    $offset = ((int)$packnum - 1) * (int)$packlength;
                    //读取文件内容
                    $fp = fopen($file, 'r');
                    fseek($fp, $offset, SEEK_CUR);
                    $thedata = fread($fp, $packlength);
                    fclose($fp);

                    $msglen = sprintf("%04x", strlen($thedata));
                    $itemstr = "2012" . $msglen . bin2hex($thedata);
                }

                break;

            case 'getWifiChannel':

                $itemstr = /*'ff000001' . $data['option']['no'] . */'70010000';

                //$retarry['data']['item2'] = '70010000';
                break;

            case 'setWifiChannel':

                $channel = sprintf('%04x', 0x00 + $data['option']['channel']);
                $itemstr = /*'ff000001' . $data['option']['no'] . */'70000002' . $channel;
                //$retarry['data']['item2'] = '70010000';
                break;

            case 'getWifiSSID':
                $data['option']['no'] = $data['option']['no1'];
                $itemstr = /*'ff000001' . $data['option']['no1'] . */'70030001' . $data['option']['no2'];
                break;

            case 'setWifiSSID':
                // SSID字符串长度
                $len = sprintf('%02x', strlen($data['option']['ssid']) / 2);

                //dump('1:' . $len);
                // 元素总长
                $totalLen = sprintf('%04x', strlen($data['option']['no2'] . $len . $data['option']['ssid']) / 2);
                //dump('2:' . $totalLen);
                $data['option']['no'] = $data['option']['no1'];
                $itemstr = /*'ff000001' . $data['option']['no1'] . */'7002' . $totalLen . $data['option']['no2'] . $len . $data['option']['ssid'];
                //dump($itemstr);
                break;
            default :
                echo "============other===========\n";
                $itemstr = '00000000';
                break;
        }


        if (in_array($data['option']['do'], ['setWifiChannel', 'getWifiChannel', 'setWifiSSID', 'getWifiSSID'])) {
            // 多设备支持
            //dump($data['option']['multi'], $data['option']['no']);
            if ($data['option']['multi']) $itemstr = 'ff000001' . $data['option']['no'] . $itemstr;

            dump($data['option']['do'], $itemstr);
        }

        $retarry['data']['item1']=$itemstr;
        return $retarry;
    }

    /**
     * 设备控制响应
     * @param $data
     * @return array|bool
     * @Created by: xiebaoxin
     * @DateTime: 2018/4/26 17:34
     */
    private function controlResponse($data)
    {

        echo "============controlResponse===========\n";

        if(!isset($data['body']) ) return false;

        if(isset($data['header']['upgradesv']) && $data['header']['upgradesv'] == 1)
            $framedata['body']=$data['body'];
        else
        {
            $retxorBody = $this->md5decode($data['body']);
            echo $retxorBody."\n";
            if(isset($retxorBody) && !empty($retxorBody)) {
                $rdata = array();
                $rdata['rawstr']=$retxorBody;
                $framedata['body'] = $this->subparsein($retxorBody);//解析子元素
                //dump($framedata['body']);
            }
        }
        if(isset($framedata['body'])){

            foreach ($framedata['body'] as $vl) {
                switch (strtoupper($vl['type'])) {
                    case '4000': //设备设置发送间隔
                        $frameHeadstr1 = sprintf('%08d', base_convert(substr($vl['datastr'], 0, 2), 16, 2));

                        $ap = substr($frameHeadstr1, 2, 2);
                        $sta = substr($frameHeadstr1, 4, 2);
                        $rf = substr($frameHeadstr1, 6, 2);
                        echo $ap."|".$sta."|".$rf;
                        $rdata['data'] = ['ap' => $ap, 'sta' => $sta, 'rf' => $rf];
                        $rdata['fromcmd']='setdatatimer';
                        break;
                    case '4001'://获取发送间隔
                        $ap = substr($vl['datastr'], 0, 4);
                        $sta = substr($vl['datastr'], 4, 4);
                        $rf = substr($vl['datastr'], 8, 4);
                        $rdata['data'] = ['ap' => hexdec($ap), 'sta' => hexdec($sta), 'rf' => hexdec($rf)];
                        $rdata['fromcmd']='getdatatimer';
                        break;
                    case '4002'://设置数据上报方式
                        $retmap=['00','成功','失败','命令不支持'];
                        $rdata['data'] =$retmap[hexdec(substr($vl['datastr'], 0, 2))];
                        $rdata['fromcmd']='setdatauptype';
                        break;
                    case '4003'://获取数据上报方式
                        $retmap=['00','处理','不处理','命令不支持'];
                        $rdata['data'] =$retmap[hexdec(substr($vl['datastr'], 0, 2))];
                        $rdata['fromcmd']='getdatauptype';
                        break;

                    case "2004": //设置重启和复位
                        $retmap=['00','成功','失败','命令不支持'];
                        $rdata['data'] =$retmap[hexdec(substr($vl['datastr'], 0, 2))];
                        $rdata['fromcmd']='setreboot';
                        break;
                    case "2005"://设置域名和端口
                        $framestr = sprintf('%08d', base_convert(substr($vl['datastr'], 0, 2), 16, 2));
                        $retmap=['00','成功','失败','命令不支持'];
                        $rdata['cfd'] = $retmap[hexdec(substr($framestr, 4, 2))];
                        $rdata['data'] = $retmap[hexdec(substr($framestr, 6, 2))];

                        $rdata['fromcmd']='setdnsport';
                        break;
                    case "2006": //获取域名和端口
                        $cfgdnslen= hexdec(substr($vl['datastr'], 0, 2));
                        $cfgdns=substr($vl['datastr'], 2, $cfgdnslen*2);
                        $cfgdnsport=substr($vl['datastr'], 2+($cfgdnslen*2), 4);
                        $rdata['scfdns'] =hex2ascii(str_split($cfgdns,2)).":".hexdec($cfgdnsport);

                        $startpos=($cfgdnslen+3)*2;
                        $dtdnslen= hexdec(substr($vl['datastr'], $startpos, 2));
                        $dtdns=substr($vl['datastr'], $startpos+2, $dtdnslen*2);
                        $dtdnsport=substr($vl['datastr'], $dtdnslen*2+$startpos+2, 4);
                        $rdata['datadns'] =hex2ascii(str_split($dtdns,2)).":".hexdec($dtdnsport);
//                        echo " \n".$cfgdnslen."--".$dtdnslen."|".$startpos." \n";

                        $rdata['fromcmd']='getdnsport';
                        break;
                    case "2007"://设置IP和端口
                        $framestr = sprintf('%08d', base_convert(substr($vl['datastr'], 0, 2), 16, 2));
                        $retmap=['00','成功','失败','命令不支持'];
                        $rdata['cfd'] = $retmap[hexdec(substr($framestr, 4, 2))];
                        $rdata['data'] = $retmap[hexdec(substr($framestr, 6, 2))];

                        $rdata['fromcmd']='setipport';
                        break;
                    case "2008": //获取IP和端口
                        $rdata['fromcmd']='getipport';
                        $rdata['data'] =$vl['datastr'];// substr($vl['datastr'],0,2);
                        $rdata['datascf'] =hexdec(substr($vl['datastr'],0,2)).".".hexdec(substr($vl['datastr'],2,2)).".".hexdec(substr($vl['datastr'],4,2)).".".hexdec(substr($vl['datastr'],6,2)).":".hexdec(substr($vl['datastr'],8,4));// substr($vl['datastr'],0,2);
                        $rdata['datadt'] =hexdec(substr($vl['datastr'],12,2)).".".hexdec(substr($vl['datastr'],14,2)).".".hexdec(substr($vl['datastr'],16,2)).".".hexdec(substr($vl['datastr'],18,2)).":".hexdec(substr($vl['datastr'],20,4));
                        break;
                    case "2009"://设置地址策略
                        $framestr = sprintf('%08d', base_convert(substr($vl['datastr'], 0, 2), 16, 2));
                        $retmap=['00','成功','失败','命令不支持'];
                        $rdata['cfd'] = $retmap[hexdec(substr($framestr, 4, 2))];
                        $rdata['data'] = $retmap[hexdec(substr($framestr, 6, 2))];

                        $rdata['fromcmd']='setaddroptin';
                        break;
                    case "200A": //获取地址策略
                        $retmap=['00','域名','IP'];
                        $rdata['cfd'] = $retmap[hexdec(substr($vl['datastr'], 0, 2))];
                        $rdata['data'] = $retmap[hexdec(substr($vl['datastr'], 2, 2))];

                        $rdata['fromcmd']='getaddroptin';
                        break;

                    case "200C": //获取终端工作模式
                        $parmarr=['00','成功','失败','命令不支持','模式不支持'];
                        $rdata['data'] = $parmarr[hexdec(substr($vl['datastr'], 0, 2))] ;// substr($vl['datastr'],0,2);
                        $rdata['fromcmd']='setworkmode';
                        break;

                    case "200D"://设置过滤策略
                        $retmap=['00','成功','失败','命令不支持'];
                        $rdata['data'] =$retmap[hexdec(substr($vl['datastr'], 0, 2))];

                        $rdata['fromcmd']='setfiltermode';
                        break;
                    case "200E"://获取过滤策略
                        $rdata['len']= hexdec(substr($vl['datastr'], 0, 2));
//                        if(mod($slen,6)==0  elseif(mod($slen,8)==0){}
                        $rdata['data'] =substr($vl['datastr'],2);
                        $rdata['fromcmd']='getfiltermode';
                        break;

                    case "2010": //设置升级
                        $parmarr=['0','成功','文件较大','硬件不兼容','不支持命令','未知错误'];
                        $optid=hexdec(substr($vl['datastr'],0,2));
                        $rdata['code'] =$optid;
                        $rdata['data'] =$parmarr[$optid];
                        $rdata['fromcmd']='setupdate';
                        break;
                    case "1011": //读取设备升级的数据请求
                        $deviceID = hex2ascii(str_split(substr($vl['datastr'], 0, 36), 2));
                        $firmware = hex2ascii(str_split(substr($vl['datastr'], 36, 60), 2));
                        $packlength = hexdec(substr($vl['datastr'], 96, 4));
                        $status = hexdec(substr($vl['datastr'], 100, 2));
                        $rdata['data'] = array(
                            'deviceID' => $deviceID,
                            'frimware' => $firmware,
                            'packlength' => $packlength,
                            'status' => $status,
                        );

                        $rdata['fromcmd']='askupdatedata';
                        break;
                    case "1012": //设备升级包请求序号
                        $rdata['data'] =hexdec(substr($vl['datastr'],0,4));
                        $rdata['fromcmd']='getupdatenum';
                        break;
                    case "1013": //设备升级结果通知
                        $parmarr=['00','成功','失败','校验错误'];
                        $optid=hexdec(substr($vl['datastr'],0,2));
                        $rdata['code'] =$optid;
                        $rdata['data'] =$parmarr[$optid];
                        $rdata['fromcmd']='endupdate';
                        break;
                    case '8001': // 获取WIFI信道列表
                        $rdata['data'] = $vl['datastr'];
                        $rdata['body'] = $vl;
                        $rdata['fromcmd'] = 'getWifiChannel';
                        break;
                    case '8000': // 设置WIFI信道列表
                        $rdata['data'] = $vl['datastr'];
                        $rdata['body'] = $vl;
                        $rdata['fromcmd'] = 'setWifiChannel';
                        break;
                    case '8002': // 设置WIFI SSID
                        $rdata['data'] = $vl['datastr'];
                        $rdata['body'] = $vl;
                        $rdata['fromcmd'] = 'setWifiSSID';
                        break;
                    case '8003': // 获取WIFI SSID
                        $rdata['data'] = pack('H*', $vl['datastr']);
                        $rdata['body'] = $vl;
                        $rdata['fromcmd'] = 'getWifiSSID';
                        break;
                    case 'FF01':
                        $rdata['data'] = $vl['datastr'];
                        $rdata['body'] = $vl;
                        // 多外设 10, 20, 30, 40
                        if ($vl['datastr'] == 40) {
                            $rdata['data'] .= '---多外设命令后面，没有具体的扩展类型命令';
                            $rdata['fromcmd'] = 'multi';
                        } elseif ($vl['datastr'][1] == 2) {
                            $rdata['data'] .= '---设备端没有该设备编号, 最大编号为:' . ($vl['datastr'][0]);
                            $rdata['fromcmd'] = 'multi';
                        } elseif ($vl['datastr'] == 30) {
                            $rdata['data'] .= '---设备端没有该类设备';
                            $rdata['fromcmd'] = 'multi';
                        }
                        break;
                    default:
                        $rdata['data'] = $vl['datastr'];
                        $rdata['body'] =$vl;
                        $rdata['fromcmd']='notfind';
                        break;
                }

                //break;

            }
        }

        return $rdata;

    }

    /**
     * 查询故障信息或故障处理
     * @param $data
     * @return array|bool
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/25 14:47
     */
    private function erroResponse($data)
    {
        echo "============erroResponse===========\n";
        $retarr=array(
            'header'=>$data['header'],
            'option'=>isset($data['option'])?$data['option']:[],

            'token'=>$this->token
        );

        if(isset($data['option'])){
            if($data['option']['do']=='geterro')
                $retarr['data']=['item1'=>'00400000'];//查询故障信息
            elseif($data['option']['do']=='responseerro') {
                $retarr['data'] = ['item1' => '00430001' . sprintf("%02x", $data['option']['data'])];
                //故障响应 responseerro 1知悉2设备自行处理3忽略4安制定方案
            }elseif($data['option']['do']=='seterro'){
                $retarr['data'] = ['item1' => '00440001' . sprintf("%02x", $data['option']['data'])];
                //故障处理  seterro 1自行2重启3复位4禁用5忽略6收到
            }

        }

        return $retarr;
    }

    /**
     * 故障报告信息
     * @param $data
     * @return array|bool
     * @Created by: xiebaoxin
     * @DateTime: 2018/4/27 13:55
     */
    private function erroreport($data)
    {
//        echo "============erroreport===========\n";
        if(!isset($data['body']) ) return false;
        $retxorBody = $this->md5decode($data['body']);

        if(isset($retxorBody) && !empty($retxorBody)){
            $rdata=array();
            $framedata['body'] = $this->subparsein($retxorBody);//解析子元素

            foreach ($framedata['body'] as $vl){
                $temprdata=[];
                $temprdata[]['errtype']=$vl['type'];
                switch ($vl['type']){
                    case '0041' :
                        $parmarr=['良好','轻微','中度','严重','极度严重'];
                        $temprdata[]['data'] =$parmarr[hexdec(substr($vl['datastr'],0,2))];//00良好01轻微02中度03严重04安全
                        break;
                    case '0042':
                        $temprdata[]['data'] = $vl['datastr'];//特殊故障信息 5个字节
                        $temstring=sprintf('%08d', base_convert(substr($vl['datastr'], 0, 2), 16, 2));
                        $temprdata[]['cfgsv']= $temstring;
                        $temprdata[]['cfgsv']['chk'] = substr($temstring, -1)==1?"配置服务上次校验异常":"配置服务上次校验正常";
                        $temprdata[]['cfgsv']['cmd'] = substr($temstring, -2,1)==1?"配置服务上次命令异常":"配置服务上次命令正常";
                        $temprdata[]['cfgsv']['run'] = substr($temstring, -3,1)==1?"配置服务上次执行异常":"配置服务上次执行正常";
                        $temprdata[]['cfgsv']['ech'] = substr($temstring, -4,1)==1?"配置服务响应超时":"配置服务响应正常";
                        $temprdata[]['cfgsv']['com'] = substr($temstring, -5,1)==1?"配置服务通讯异常":"配置服务通讯正常";

                        $temstring=sprintf('%08d', base_convert(substr($vl['datastr'], 2, 2), 16, 2));
                        $temprdata[]['datasv']= $temstring;
                        $temprdata[]['datasv']['chk'] = substr($temstring, -1)==1?"数据服务上次校验异常":"数据服务上次校验正常";
                        $temprdata[]['datasv']['cmd'] = substr($temstring, -2,1)==1?"数据服务上次命令异常":"数据服务上次命令正常";
                        $temprdata[]['datasv']['run'] = substr($temstring, -3,1)==1?"数据服务上次执行异常":"数据服务上次执行正常";
                        $temprdata[]['datasv']['ech'] = substr($temstring, -4,1)==1?"数据服务响应超时":"数据服务响应正常";
                        $temprdata[]['datasv']['com'] = substr($temstring, -5,1)==1?"数据服务通讯异常":"数据服务通讯正常";
                        $temprdata[]['datasv']['cnt'] = substr($temstring, -6,1)==1?"数据服务连接失败":"数据服务连接正常";

                        $temstring=sprintf('%08d', base_convert(substr($vl['datastr'], 4, 2), 16, 2));
                        $temprdata[]['device']= $temstring;
                        $temprdata[]['device']['rf'] = substr($temstring, -1)==1?"RF模块故障":"RF模块正常";
                        $temprdata[]['device']['wifi'] = substr($temstring, -2,1)==1?"WIFI模块故障":"WIFI模块正常";
                        $temprdata[]['device']['bt'] = substr($temstring, -3,1)==1?"BT模块故障":"BT模块正常";
                        $temprdata[]['device']['gps'] = substr($temstring, -4,1)==1?"GPRS模块故障":"GPRS模块正常";
                        $temprdata[]['device']['net'] = substr($temstring, -5,1)==1?"网卡模块故障":"网卡模块正常";
                        $temprdata[]['device']['oth'] = substr($temstring, -6,1)==1?"未知故障":"未知正常";
                        $temprdata[]['device']['cch'] = substr($temstring, -7,1)==1?"缓冲区溢出":"缓冲区正常";

                        $temstring=sprintf('%08d', base_convert(substr($vl['datastr'], 6, 2), 16, 2));
                        $temprdata[]['tempre']= $temstring;
                        $temprdata[]['tempre']['up'] = substr($temstring, -1)==1?"温度急剧上升":"温度上升正常";
                        $temprdata[]['tempre']['dow'] = substr($temstring, -2,1)==1?"温度急剧下降":"温度下降正常";
                        $temprdata[]['tempre']['hig'] = substr($temstring, -3,1)==1?"温度过高":"温度高正常";
                        $temprdata[]['tempre']['low'] = substr($temstring, -4,1)==1?"温度过低":"温度低正常";

                        $temstring=sprintf('%08d', base_convert(substr($vl['datastr'], 8, 2), 16, 2));
                        $temprdata[]['power']= $temstring;
                        $temprdata[]['power']['rf'] = substr($temstring, -1)==1?"电压波动过大":"电压波动正常";
                        $temprdata[]['power']['wifi'] = substr($temstring, -2,1)==1?"电量不足随时关机":"电量正常";


                        break;

                    default:
                        $temprdata[]['data'] = $vl['datastr'];

                }

            }
            $rdata['data']=$temprdata;
            $rdata['rawstr']=$retxorBody;
        }
        $rdata['fromcmd']='geterro';
//        var_dump($rdata);
        return $rdata;


    }

    /**
     * 发送断开请求或断开响应
     * @param $data
     * @return array
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/25 14:58
     */
    private function closeAsk($data)
    {
        if($data['header']['td']=='01'){
            //探霸过来的  解析结果
            echo "============closeAsk===========\n";
            $rdata=array();
            $retxorBody = $this->md5decode($data['body']);
            $framedata['body'] = $this->subparsein($retxorBody);//解析子元素
//            var_dump($framedata['body']);
            foreach ($framedata['body'] as $vl){
                switch ($vl['type']){
                    case '0030' :
                        $rdata['datastr']=$vl['datastr'];//substr($vl['datastr'],0,2);//01正常02重连03重发秘钥04强制断开
                        $rdata['data'] =substr($vl['datastr'],0,2);
                        $rdata['fromcmd']='closeAsk';
                        break;
                    default:
                        $rdata[]['data']=$vl['datastr'];
                }
            }
            $rdata['rawstr']=$retxorBody;
            return $rdata;
        }elseif($data['header']['td']=='10'){
//            var_dump($data);
            if(isset($data['option']['do'])){
                $retarr=array(
                    'header'=>$data['header'],
                    'option'=>isset($data['option'])?$data['option']:[],

                    'token'=>$this->token
                );

                //发给探霸的  发送命令
                if($data['option']['do']=='setclose'){
                    echo "============setclose===========\n";
                    $retarr['data']= ['item1' =>'00300001'.sprintf("%02x",$data['option']['data'])];//1正常2重连3重发秘钥4强制断开
                }elseif ($data['option']['do']=='setdowntime' && isset( $data['option']['cfg']) && isset( $data['option']['data'])){
                    echo "============setdowntime===========\n";
                    $string1 = sprintf("%04x", $data['option']['cfg']);
                    $string2 = sprintf("%04x",  $data['option']['data']);
                    $retarr['data'] =  ['item1' =>'100B0004'. $string1.$string2];
                }

                return $retarr;
            }

        }

    }

    /**
     * 断开请求
     * @param $data
     * @return array|bool
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/25 14:47
     */
    private function CloseAnswer($data)
    {
        if($data['header']['td']=='01'){
            //探霸过来的  解析结果
            echo "============CloseAnswer===========\n";
            $rdata=array();
            $retxorBody = $this->md5decode($data['body']);
            $framedata['body'] = $this->subparsein($retxorBody);//解析子元素

            foreach ($framedata['body'] as $vl){
                switch (strtoupper($vl['type'])){
                    case '0031' :
                        $rdata['datastr']=$vl['datastr'];//substr($vl['datastr'],0,2);
                        $retmap=['00','正常','已收到','拒绝'];
                        $rdata['data'] =$retmap[hexdec(substr($vl['datastr'],0,2))];
                        $rdata['fromcmd']='setclose';
                        break;
                    case '200B':
                        $framestr = sprintf('%08d', base_convert(substr($vl['datastr'], 0, 2), 16, 2));
                        $retmap=['00','成功','失败','命令不支持'];
                        $rdata['cfd'] = $retmap[hexdec(substr($framestr, 4, 2))];
                        $rdata['data'] = $retmap[hexdec(substr($framestr, 6, 2))];

                        $rdata['datastr']=$vl['datastr'];//substr($vl['datastr'],0,2);//终端下线时间
                        $rdata['fromcmd']='setdowntime';
                        break;
                    default:
                        $rdata[]['data']=$vl['datastr'];
                }

            }
            $rdata['rawstr']=$retxorBody;
            return $rdata;

        }elseif($data['header']['td']=='10'){
            //发给探霸的  发送命令 响应
            if($data['option']['do']=='setcloseresponse'){
                echo "============setcloseresponse===========\n";
                $itemstr='00310001'.sprintf("%02x",$data['option']['data']);//1正常2收到3拒绝断开
            }

            echo $itemstr."\n";

            return array(
                'header'=>$data['header'],
                'option'=>isset($data['option'])?$data['option']:[],
                'data'=>[
                    'item1'=>$itemstr
                ],
                'token'=>$this->token
            );

        }





    }

    private function baseControl($data)
    {
        if(isset($data['option'])){
            switch ($data['option']['do']){
                case "sdtoken":
//                    echo "============sdtoken===========\n";
                    if(isset($data['option']['token']))
                        $itemstr='10010004'.sprintf("%08x",$data['option']['token']);//发送token
                    else
                        $itemstr='10010004'.sprintf("%08x",$this->token);//发送token
                    break;
                case "heart":
//                    echo "============heart===========\n";
                    $itemstr='10000000';//发送心跳
                    break;
                case "respheart":
//                    echo "============heart===========\n";
                    $itemstr='20000000';//发送心跳
                    break;
                case "setheart":
                    echo "============setheart===========\n";
                    if(isset( $data['option']['data'])){
                        $string = sprintf("%04x", $data['option']['data']);
                        $itemstr='10020002'.$string;//设置心跳
                    }else
                        return false;

                    break;
                case "getheart":
                    echo "============getheart===========\n";
                    $itemstr='10030000';//获取心跳
                    break;

                default:
                    echo "============baseControl===========\n";
                    $itemstr='10000000';
            }


        }

        return array(
            'header'=>$data['header'],
            'option'=>isset($data['option'])?$data['option']:[],
            'data'=>[
                'item1'=>$itemstr
            ],
            'token'=>$this->token
        );

    }

    /**
     * 基础控制帧-心跳
     * @param $data
     * @return array
     * @Created by: xiebaoxin
     * @DateTime: 2018/4/28 11:03
     */
    private function getbasecontrolinfo($data)
    {
        $datastr = $this->subparsein($data);
        $rdata=[];
        $parmarr=['00','成功','失败','不支持'];
        foreach ($datastr as $vl){
            if($vl['type']){
                switch ($vl['type']){

                    case '1000' :
                        $rdata['data']=$vl['datastr'];//心跳
                        $rdata['fromcmd']='heart';
                        break;
                    case '2000' :
                        $rdata['data']=$vl['datastr'];//心跳
                        $rdata['fromcmd']='respheart';
                        break;

                    case '2001' :
                        $rdata['data']='00';//token
                        $rdata['fromcmd']='sdtoken';
                        break;
                    case '2002' : ////设置心跳间隔
                        $rdata['data']=$parmarr[hexdec($vl['datastr'])];
                        $rdata['fromcmd']='setheart';
                        break;
                    case '2003':
                        $rdata['data']=hexdec($vl['datastr']);
                        $rdata['fromcmd']='getheart';//获取心跳间隔
                        break;
                    default:

                        $rdata['data']=$datastr;//$vl['datastr'];
                        $rdata['fromcmd']='otherheart';
                }
            }
        }
        $rdata['rawstr']=$data;
        return $rdata;

    }


    private function  dataResopnse($data)
    {
       if(!isset($data['option']['rfid']) ) return false;
        echo "\n ============dataResopnse===========\n";
        $data['header']['dt']='10';
        $data['header']['sdt']='0001';
        return array(
            'header'=>$data['header'],
            'option'=>$data['option'],
            'data'=>[
                'item1'=>'A1020008'.$data['option']['rfid']
            ],
            'token'=>$this->token
        );

    }

    private function  dataAsk($data)
    {
        $databodystr=$data['body'];//探霸原始数据
        $retxorBody = $this->md5decode($databodystr);
        $data['encodebodystr']=$retxorBody ;
        return $data;
    }

    /**
     * 接收的数据类帧-数据解析
     * @param $data
     * @return mixed
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/23 14:04
     */
    private function getdataReceive($data)
    {
        $databodystr=$data['body'];//探霸原始数据
        $datas = array();

        try{
            $retxorBody =$this->md5decode($databodystr);//$this->php_md5decode($databodystr);//

            if(isset($retxorBody) && !empty($retxorBody)){
                $datatype = substr($retxorBody,0, 4);
                if (in_array($datatype, ['a000', 'a001', 'a002'])){
                    if ($datatype=='a002') {
                        if(strlen($retxorBody)<=8){
                            echo  " \n ".$retxorBody;
                        }
                        else{
                            $datas=$this->uploadRfData(substr($retxorBody, 8), 'a002');
                        }
                    }else{
                        $string=$retxorBody;
//                        echo "\n ---------------str----------------------\n";
//                        echo "\n ----0:-----{$string}-----------------\n";
                        while (true) {
                            //解析子元素
                            $dtype=substr($string, 0, 4); //帧头2个字节为类型
                            $offset =2 *hexdec(substr($string, 4, 4)); //总包长
                            if( !$offset ||!isset($offset)||empty( $offset)|| $offset<16) {
                                break;
                            }

                            $dataitem0 = substr($string, 8, $offset);//总长度
                            $nextstr=$dataitem0;
                            if($dtype=='a000'){
                                while (true) {
                                    $apstrlen = hexdec(substr($nextstr,14,2));
                                    $len= 22+$apstrlen*2;
                                    $dataitem=substr($nextstr, 0,$len);
//                                    echo "\n ---a000------{$dataitem}-----------------\n";
                                    $datas[]= $this->uploadApData($dataitem, 'a000');

                                    $nextstr=substr($nextstr, $len);
                                    if (false == $nextstr || $nextstr=="" || empty($nextstr)) break;
                                }

                            }
                            elseif($dtype=='a001'){
                                while (true) {
                                    $ifhasap= substr($nextstr,16,2);
                                    $len= $ifhasap=='01' ?30:18;
                                    $dataitem=substr($nextstr, 0,$len);
//                                    echo "\n ----a001-----{$dataitem}-----------------\n";
                                    $datas[]=$this->uploadStaData($dataitem, 'a001');

                                    $nextstr=substr($nextstr, $len);
                                    if (false == $nextstr || $nextstr=="" || empty($nextstr)) break;
                                }

                           }
                            else
                                break;

                            $string=substr($string, $offset+8);
                            if (false == $string || $string=="" || empty($string)) break;

                        }
//                        echo "---------------end---------------------\n";
                    }

                }
                else{
                    $datas=['type'=>'typeErro','rawstr'=>$retxorBody,'errostr'=>$databodystr];
                }

            }
            else{
                echo "\n----:-{$databodystr} |---->-{$retxorBody}----\n";
            }
        }
        catch (\Exception $e) {
            echo '解析错误：'.$databodystr." \n";
        }


        $retxorBody=null;
        unset($retxorBody);

        return $datas;
    }


    /**
     *  上传AP数据
     *  @param string $body 消息体
     *  @return array $shuju 解析成功的数组数据
     *  @code A000
     *  @author qmx
     */
    function uploadApData($row,$msgType)
    {
        $frmtype=substr($row,0,2);
        $bssid = substr($row,4,12);

        $ssid_len = hexdec(substr($row,14,2));
        $ssid = hex2ascii(str_split(substr($row,16, ($ssid_len * 2)), 2));

        $channel = hexdec(substr($row,(16 + ($ssid_len * 2)),2));
        $rssi = rssi(hexdec(substr($row,(18 + ($ssid_len * 2)),2)));
        $is_hidden = substr($row,(20 + ($ssid_len * 2)),2);
        $array = array("type"=>$msgType,"frametype"=>$frmtype,"mac"=>$bssid,"rssi"=>$rssi,"ssid_len"=>$ssid_len,"ssid"=>$ssid,"channel"=>$channel,"is_hidden"=>$is_hidden);
        return $array;
    }

    /**
     * 上传STA数据
     *  @param string $body 消息体
     *  @return array $shuju 解析成功的数组数据
     *  @code A001
     *  @author qmx
     */
    public function uploadStaData($row,$msgType)
    {
        $frmtype=substr($row,0,2);
        $mac=substr($row,2,12);
        $rssi=rssi(hexdec(substr($row,14,2)));
        $Has_ap =substr($row,16,2);
        $ApMac=$Has_ap =='00'?"00":substr($row,18,12);
        $array=array("type"=>$msgType,"frametype"=>$frmtype,"mac"=>$mac,"rssi"=>$rssi,"Has_ap"=>$Has_ap,"ApMac"=>$ApMac);
        return $array;
    }

    /**
     * 上传RF数据
     *  @param string $body 消息体
     *  @return array $shuju 解析成功的数组数据
     *  @code A002
     *  @author qmx
     */
    public function uploadRfData($body,$msgType)
    {
        $data=str_split($body,26);
        $shuju=array();
        for($i=0;$i<count($data);$i++)
        {
            if(isset($data[$i]) && strlen($data[$i])>0){
                $deviceID= substr($data[$i],0,16);
                $battery=substr($data[$i],16,4);
                $sendRssi=  rssi(hexdec(substr($data[$i],20,2)));
                $rssi= rssi(hexdec(substr($data[$i],22,2)));
                $urgentStatus=(int)substr($data[$i],24,2);// ? '紧急' : '正常';
                $array=array("type"=>$msgType,"type"=>$msgType,"mac"=>$deviceID,"battery"=>$battery,"sendRssi"=>$sendRssi, "rssi"=>$rssi,"urgentStatus"=>$urgentStatus);//,'rawstr'=>$body
                $shuju[]=$array;
            }
            else
                break;

        }
        return $shuju;
    }


    /**
     * 校验和消息
     * @param $data
     * @param $token
     * @return bool|null|string
     */
    public function xorencode($datastr, $token=0)
    {
        $su=0;
        foreach (str_split($datastr, 2) as $v) {
            $su += base_convert($v, 16, 10);
        }
        $retstr=sprintf("%04x",$su);
        if(strlen($retstr)>4)
            $retstr=substr($retstr,-4);
        return $retstr;
    }


    public function pushhex($string)
    {
        $string=implode(" ", str_split($string, 2));
        $checksum = 0x0;
        $cmdStrArr = explode(' ',$string);
        for($i=0;$i<count($cmdStrArr);$i++){
            $cmdHex[] = chr(hexdec($cmdStrArr[$i]));
        }
        // $cmdHex[] = chr($checksum);
        $cmdHex=implode($cmdHex);
        return $cmdHex;
    }


    /**
     * 加密消息
     * @param $data
     * @param $token
     * @return bool|null|string
     */
    public function encryption($string,$token)
    {
        $string =$string."0000000000000000";

        $data=pack("H*",$string);
        $value=bin2hex(encrypt_data($data, $token,64));

        if($value!="31") return false;
        return bin2hex($data);
//原来旧版本的加密协议
        $status=substr($value,0,2);   //截取前两个字符判断是否解密成功 16进制31（1 ascii）成功、30（0 ascii）失败
        if($status!="31") return false;
        return substr($value, 2);

    }

    /**
     * MD5解密
     * @param $string
     * @Created by: xiebaoxin
     * @DateTime: 2018/3/26 9:04
     */
    public function md5decode($string)
    {
//        echo "------------st--------".PHP_EOL;
//        echo $string.PHP_EOL;
//        echo $this->token.PHP_EOL;
        $databodystr=substr($string,0,-16);//扣除校验段数据段
        $xorBody= bin2hex(xor_code(pack('H*', $databodystr), pack('H*', dechex($this->token))));
//        $string = bin2hex(decode_data($string, $token, $md5type));   //解密


//        echo $xorBody.PHP_EOL;
//        echo "-----------end--------".PHP_EOL;
        if (!$xorBody) return false;
        return $xorBody;
    }


    function hexCalcSum($str)
    {
        $sum = 0;
        $str = str_split($str, 2);
        foreach ($str as $r) $sum += hexdec($r);
        return $sum;
    }

    /**
     * 数组转ascii码
     * @param array $data
     * @return null|string
     */
    public function hex2char(array $data)
    {
        $chars = null;
        foreach ($data as $r) $chars .= chr(hexdec($r));
        return $chars;
    }

    // $data = implode(" ", str_split($str, 2)); //string(41) "04 08 00 0e 00 04 00 01 00 00 32 00 01 01"


    public function php_md5decode($string)
    {

        $str=substr($string,0,-16);//扣除校验段数据段;
        $key=dechex($this->token);//

        $crytxt = '';
        $data=str_split($str,2);//每次取1个字节
        $keydata=str_split($key,2);//每次取1个字节
        $len=count($data);
        for($i=0;$i<$len;$i++)
        {
            $k =$i % 4 ;
      /*    //echo ($i%2)."|".$data[$i]."|".$keydata[$k]."|".PHP_EOL; //偶数
            if(($i%2)){
                if(($i + $len)<=15)
                    $crytxt .=pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^( pack('h*',dechex($i + $len)) & pack('H*', "ff"));
                else
                    $crytxt .=pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^( pack('H*',dechex($i + $len)) & pack('H*', "ff"));
            }
            else
            {//偶数
                if($i<=15)
                    $crytxt .= pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^ pack('h*',dechex($i)) ;
                else
                    $crytxt .= pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^ pack('H*',dechex($i));
            }*/
            if(($i%2)){
                $newi=$i + $len;
                if($newi<=15 )
                    $crytxt .=pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^( pack('h*',dechex($newi)) & pack('h*', "ff"));
                else
                    $crytxt .=pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^( pack('H*',substr(sprintf("%04x",$newi),-2)) & pack('h*', "ff"));

            }
            else
            {//偶数
                if($i<=15 )
                    $crytxt .= pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^ pack('h*',dechex($i)) ;

                else
                    $crytxt .= pack('H*',$data[$i]) ^ pack('H*',$keydata[$k]) ^( pack('H*',substr(sprintf("%04x",$i),-2)));

            }

        }

        return bin2hex($crytxt);

    }

    /*
** 异或加解密
** 解析服务器数据时,对数据部分解密
** 发送探测的数据时,对数据部分加密
** len:需要加解密的数据长

void Xor_Enc(unsigned char *data, unsigned char Token_Packet[], unsigned char len)
{
unsigned char i;
for(i=0; i<len; i++)
{
if(i % 2) data[i] = data[i] ^ Token_Packet[i%4] ^ ((len + i) & 0xff);
else      data[i] = data[i] ^ Token_Packet[i%4] ^ i;
}
}

*/

}
